package calculator;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.GridPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;
/**
*
* @author amnairfan
*/
public class Calculator extends Application {
static Button[][] btn = new Button[5][5];
static TextField display = new TextField();
static double storeNum=-1;
static String storeSign=null;
static String enteredNum;
static Double num;
static String sign;
static boolean started;
static double backupNum1,backupNum2;
static String backupSign;
static double backupStoreNum;
static String backupStoreSign;
static boolean atSingleCalc;
static boolean atEqualCalc;
static boolean atExponential;
static int expNum;
static boolean nan;
@Override
public void start(Stage primaryStage)
{
GridPane pane = new GridPane();
modifyPane(pane);
display = new TextField();
display.setEditable(false);
display.setAlignment(Pos.CENTER_RIGHT);
setDisplay(pane,"0");
intializeButtons();
setButtonDisplay(pane);
Scene scene = new Scene(pane, 425, 525);
scene.setFill(Color.LIGHTBLUE);
primaryStage.setScene(scene);
primaryStage.setTitle("Calculator v0.0.1");
primaryStage.show();
clear();
keyPressEvents();
buttonClickEvents();
}
public static void buttonClickEvents(){
for (int row=1;row<=4;row++){
for( int col=0;col<=2;col++){
if((row==4&&col==2)==false)
btn[row][col].setOnAction(e->{
Object o=e.getSource();
Button b=(Button)o;
enterNumber(b.getText());});
}
}
for (int row=1;row<=4;row++){
btn[row][3].setOnAction(e->{
Object o=e.getSource();
Button b=(Button)o;
calculateSigns(b.getText());
});
}
btn[4][2].setOnAction(e->{
calculate();
});
btn[0][4].setOnAction(e->{
back();
});
btn[0][0].setOnAction(e->{
clear();
});
btn[4][4].setOnAction(e->{
Object o=e.getSource();
Button b=(Button)o;
powerMode(b.getText());
});
for( int col=1;col<=3;col++){
btn[0][col].setOnAction(e->{
Object o=e.getSource();
Button b=(Button)o;
singleCalculation(b.getText());
});
}
for( int row=1;row<=3;row++){
btn[row][4].setOnAction(e->{
Object o=e.getSource();
Button b=(Button)o;
singleCalculation(b.getText());
});
}
}
public static void keyPressEvents(){
display.setOnKeyPressed(new EventHandler<KeyEvent>() {
public void handle(KeyEvent ke) {
String keyText=ke.getText();
String keyCode=ke.getCode().toString();
boolean isNum;
try
{
double d = Double.parseDouble(keyText);
isNum=true;
}
catch(NumberFormatException nfe)
{
isNum=false;
}
if(isNum||keyText.equals("."))
enterNumber(keyText);
if(keyText.equals("+")||keyText.equals("*")||keyText.equals("-")||keyText.equals("/"))
calculateSigns(keyText);
if(keyText.equals("=")||keyCode.equals("ENTER"))
calculate();
if(keyCode.equals("BACK_SPACE"))
back();
}
});
}
public static void powerMode(String mode){
if(mode.equals("OFF")){
btn[4][4].setText("ON");
for (int row=0;row<=4;row++){
for( int col=0;col<=4;col++){
if((row==4&&col==4)==false)
btn[row][col].setDisable(true);
}
clear();
}
}
else{
btn[4][4].setText("OFF");
for (int row=0;row<=4;row++){
for( int col=0;col<=4;col++){
if((row==4&&col==4)==false)
btn[row][col].setDisable(false);
}
}
}
}
public static void clear(){
display.requestFocus();
storeNum=-1;
storeSign=null;
enteredNum="";
num=0.0;
sign="+";
started=false;
backupNum1=0;
backupNum2=0;
backupSign=null;
backupStoreNum=0;
backupStoreSign=null;
atSingleCalc=false;
atEqualCalc=false;
atExponential=false;
display.setText("0");
nan=false;
}
public static void singleCalculation(String signType){
//chnages the current number to a differnt number with calcualtions
display.requestFocus();
if(enteredNum.equals("")){
if(signType.equals("Cos"))
enteredNum=Double.toString(Math.cos(0));
else if(signType.equals("Sin"))
enteredNum=Double.toString(Math.sin(0));
else if (signType.equals("log2"))
enteredNum=Double.toString(Math.log10(0)/Math.log10(2));
else if (signType.equals("log10"))
enteredNum=Double.toString(Math.log10(0));
else if(signType.equals("Exp")){
expNum=Integer.parseInt(enteredNum);
atExponential=true;
}
else{
enteredNum=Double.toString(Math.sqrt(0));
if(sign.equals("-"))
nan=true;
}
}
else{
if(signType.equals("Cos"))
enteredNum=Double.toString(Math.cos(Double.parseDouble(enteredNum)));
if(signType.equals("Sin"))
enteredNum=Double.toString(Math.sin(Double.parseDouble(enteredNum)));
else if (signType.equals("log2"))
enteredNum=Double.toString(Math.log10(Double.parseDouble(enteredNum))/Math.log10(2));
else if (signType.equals("log10"))
enteredNum=Double.toString(Math.log10(Double.parseDouble(enteredNum)));
else if(signType.equals("Exp")){
expNum=Integer.parseInt(enteredNum);
atExponential=true;
}
else{
enteredNum=Double.toString(Math.sqrt(Double.parseDouble(enteredNum)));
if(sign.equals("-"))
nan=true;
}
}
//sets new number as entered number and puts it on screen
if(!atExponential)
atSingleCalc=true;
if(nan)
display.setText("NaN");
else
displayResults(enteredNum);
}
public static void back(){
display.requestFocus();
if(!enteredNum.equals("")){
enteredNum=enteredNum.substring(0,enteredNum.length()-1);
displayResults(enteredNum);
}
}
public static void displayResults(String n){
display.setText(format(n));
}
public static void calculate(){
if(!atExponential)
atExponential=true;
display.requestFocus();
if(enteredNum.equals(""))
enteredNum=Double.toString(num);
if(sign.equals("+"))
num=num+Double.parseDouble(enteredNum);
else if(sign.equals("-"))
num=num-Double.parseDouble(enteredNum);
else if(sign.equals("/"))
num=num/Double.parseDouble(enteredNum);
else
num=num*Double.parseDouble(enteredNum);
if(storeSign!=null){
if(storeSign.equals("+"))
num=storeNum+num;
if(storeSign.equals("-"))
num=storeNum-num;
storeNum=-1;
storeSign=null;
}
sign="+";
enteredNum="0";
displayResults(Double.toString(num));
atEqualCalc=true;
}
public static void restoreValues(String enteredSign){
//gather 3 variables for calculation
num=backupNum1;
enteredNum=Double.toString(backupNum2);
sign=backupSign;
if(enteredSign.equals("/")||enteredSign.equals("*")){
storeSign=backupStoreSign;
storeNum=backupStoreNum;
}
}
public static void calculateSigns(String enteredSign){
//once sign is pressed continues with num produced by tan sin or equals etc as an operand
//example 3Sin+5
if(atSingleCalc){
atSingleCalc=false;
}
if(atEqualCalc){
atEqualCalc=false;
}
if(atExponential)
atExponential=false;
//this method predicts the next step.
//based on 5+3- (num)(sign)(enteredNum)(enteredSign)
//the entered sign and sign both decide the next move
//the starting value of num is 0 ad sign is +.
//every calcualation in the begining will be 0+enteredNum,enteredSign
display.requestFocus();
//Case#1: starts with a number and then a sign
//0+enteredNum,enteredSign
//usual way
if(!started && !enteredNum.equals("")){
started=true;
}
//Case#2: it starts with a sign
//Soultion: Since there is no entered number we make enteredNum as a 0
//0+0,enteredSign
//example - should result into 0-
//in future when someone enters a number it will be 0-9 which is equal to -9!!
else if (!started){
started=true;
enteredNum="0";
}
//what if the user presses the - and then *
//Detection way: enterednum will be empty
//prediction changes, in that case restore to the last values with new entered sign
//if they do in the beginging too like - and then + it works because enteredValue has changed to 0
/*Check: start calculator type - then change it to * and type 9 and click on equals, it will give
you a 0 not a -9
*/
/*else: if not changing signs, save the num and sign and entered num to the backup incase the
user changes sign*/
if(enteredNum.equals(""))
restoreValues(enteredSign);
else{
backupNum1=num;
backupNum2=Double.parseDouble(enteredNum);
backupSign=sign;
}
//Begining of predictions
//Case#1: When both signs are + or -, just calcualte
//when noth signs are * or /, just calcualte
//8 different combination work in this case - -, + +, - +, + -, * /, /*, * *, / /
if(enteredSign.equals(sign)||enteredSign.equals("+")||enteredSign.equals("-")||(sign.equals("/")&&enteredSign.equals("*"))||(sign.equals("*")&&enteredSign.equals("/"))){
if(sign.equals("+"))
num=num+Double.parseDouble(enteredNum);
else if (sign.equals("-"))
num=num-Double.parseDouble(enteredNum);
else if (sign.equals("*"))
num=num*Double.parseDouble(enteredNum);
else
num=num/Double.parseDouble(enteredNum);
}
//Case#2: where sign is + or - and new sign is a multiplication
//in this case hold calculations
//store the num and sign to use it after wards
// and make entered number the num
//Example. 3+5* (cannot be calculated as 5 can have more numbers be multiplied to it
//5+7*7= 5+49
//the stored 5 will be added later
//Case#3: when its the time for the holding number to be added or subtracted
//When user enters + or - it means its no longer waiting for a new value
//2+3*5+ (2 was stored before becuase of pemdas)
//3*5 was multipled as qualified case#1, num now equals to 15
//since 5 preceded an + or maybe equal or -, holding value can be added
//2+15 now equals 17!
else if((sign.equals("+")||sign.equals("-"))&&(enteredSign.equals("*")||enteredSign.equals("/"))){
storeNum=num;
storeSign=sign;
num=Double.parseDouble(enteredNum);
}
if(storeSign!=null&&((sign.equals("*")||sign.equals("/"))&&(enteredSign.equals("+")||enteredSign.equals("-")))){
if(storeSign.equals("+"))
num=storeNum+num;
else
num=storeNum-num;
backupStoreNum=storeNum;
backupStoreSign=storeSign;
storeSign=null;
storeNum=-1;
}
//first time for - values
sign=enteredSign;
enteredNum="";
displayResults(num.toString());
}
public static void enterNumber(String num){
display.requestFocus();
if(nan)
clear();
//resets the calculator if presses a number instead of a sign after Sin cos etc
if(atSingleCalc){
clear();
}
if(atEqualCalc){
clear();
}
if(atExponential){
atExponential=false;
Double n=Math.pow(expNum,Integer.parseInt(num));
enteredNum=Double.toString(n);
displayResults(enteredNum);
expNum=0;
}
else{
boolean allowDecimal=true;
for(int i=0;i<enteredNum.length()&&allowDecimal;i++){
if(enteredNum.charAt(i)=='.')
allowDecimal=false;
}
//for decimals
if(num.equals(".")&&enteredNum.length()==0)
enteredNum="0";
if(allowDecimal||!num.equals(".")) {
enteredNum=enteredNum+num;
display.setText(enteredNum);
}
}
}
public static void modifyPane(GridPane pane)
{
pane.setPadding(new Insets(10));
pane.setHgap(5);
pane.setVgap(5);
pane.setAlignment(Pos.CENTER);
}
public static void setDisplay(GridPane pane, String n)
{
//TextField display = new TextField();
display.setPrefWidth(175);
display.setPrefHeight(80);
display.setPromptText(n);
display.setFont(Font.font("Verdana", FontWeight.BOLD, 25));
display.setStyle("-fx-text-inner-color: black;");
pane.setColumnSpan(display, 5);
pane.setRowSpan(display, 2);
pane.add(display, 0, 0);
pane.setHalignment(display, HPos.CENTER);
}
public static void intializeButtons(){
final ImageView squareRoot = new ImageView(
new Image("file:../calculator/src/calculator/square_root.png")
);
final ImageView back = new ImageView(
new Image("file:../calculator/src/calculator/back.png")
);
btn[0][0] = new Button("C");
btn[0][1] = new Button("Sin");
btn[0][2] = new Button("Cos");
btn[0][3] = new Button("Exp");
btn[0][4] = new Button("",back);
btn[1][4]= new Button("log2");
btn[2][4]= new Button("log10");
btn[3][4]= new Button("", squareRoot);
btn[4][4]= new Button("OFF");
int count = 1;
//initializing the numbers
for(int i = 3; i > 0; i--)
{
for(int j = 0; j < 3; j++)
{
btn[i][j] = new Button("" + count);
count++;
}
}
btn[1][3] = new Button("/");
btn[2][3] = new Button("*");
//pane.setColumnSpan(btn[2][3], 2);
btn[3][3] = new Button("-");
//pane.setColumnSpan(btn[3][3], 2);
btn[4][3] = new Button("+");
//pane.setColumnSpan(btn[4][3], 2);
btn[4][2] = new Button("=");
//pane.setColumnSpan(btn[4][2], 2);
btn[4][0] = new Button("0");
//pane.setColumnSpan(btn[4][0], 2);
btn[4][1] = new Button(".");
}
public static void setButtonDisplay(GridPane pane){
for(int i = 0; i < btn.length; i++)
{
for(int j = 0; j < btn.length; j++)
{
if(btn[i][j] != null)
{
if(i == 0){
if(j==0)
btn[i][j].setStyle("-fx-background-color: brown; -fx-text-fill: white; -fx-font: bold italic 20pt \"Arial\";");
else
btn[i][j].setStyle("-fx-background-color: brown; -fx-text-fill: white; -fx-font: bold italic 14pt \"Arial\";");
}
else if( j == 3)
btn[i][j].setStyle("-fx-background-color: cornflowerblue; -fx-text-fill: white; -fx-font: bold 20pt \"Arial\";");
else if( j == 4)
btn[i][j].setStyle("-fx-background-color: darkGray; -fx-text-fill: black; -fx-font: bold 14pt \"Arial\";");
else
btn[i][j].setStyle("-fx-font: bold 20pt \"Arial\";");
btn[i][j].setPrefSize(75,75);
pane.add(btn[i][j],j,i+2);
}
}
}
}
public static String format(String s){
if(s.equals(""))
return s;
else if(s.length()!=1&&s.charAt(s.length()-1)=='0'&&s.charAt(s.length()-2)=='.')
return s.substring(0, s.length()-2);
else
return s;
}
}